//////////////////////////////////////////////////////////////////////////
//
//  E57Simple.cpp - private implementation header of E57 format reference implementation.
//
//	Copyright (c) 2010 Stan Coleby (scoleby@intelisum.com)
//	All rights reserved.
//
//  Permission is hereby granted, free of charge, to any person or organization
//  obtaining a copy of the software and accompanying documentation covered by
//  this license (the "Software") to use, reproduce, display, distribute,
//  execute, and transmit the Software, and to prepare derivative works of the
//  Software, and to permit third-parties to whom the Software is furnished to
//  do so, all subject to the following:
// 
//  The copyright notices in the Software and this entire statement, including
//  the above license grant, this restriction and the following disclaimer,
//  must be included in all copies of the Software, in whole or in part, and
//  all derivative works of the Software, unless such copies or derivative
//  works are solely in the form of machine-executable object code generated by
//  a source language processor.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
//  SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
//  FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
//  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//  DEALINGS IN THE SOFTWARE.
//
//	The Boost license Vestion 1.0 - August 17th, 2003 is discussed in
//	http://www.boost.org/users/license.html.
//
//  This source code is only intended as a supplement to promote the
//	ASTM E57.04 3D Imaging System File Format standard for interoperability
//	of Lidar Data.  See http://www.libe57.org.
//
//////////////////////////////////////////////////////////////////////////
//
//	New E57Simple.cpp
//	V5		May 18, 2010	Stan Coleby		scoleby@intelisum.com
//	V6		June 8, 2010	Stan Coleby		scoleby@intelisum.com
//  V7		July 28,2010	Stan Coleby		scoleby@intelisum.com
//  V8		Nov. 3, 2010	Stan Coleby		scoleby@intelisum.com  Added new intensityLimits and colorLimits
//
//////////////////////////////////////////////////////////////////////////
/*================*/ /*!
@mainpage

@section main_Introduction Introduction
This browser-based document describes the E57 Simple API (Application Programmer Interface) version 0.52, which is a collection of functions that help a C++ programmer wrap the E57 Foundation API.

@section main_Read_example Reading using the E57 Simple API
An example of a typical use of this interface would be as follows:
<tt><PRE>
	try
	{
// Create a ReaderImpl

		_bstr_t bsFile = sFile;			//converts Unicode to UTF-8
		e57::Reader	eReader( (char*) bsFile);

///////////////////////////////////////////////////////////////
// ACCESSING ROOT DATA

// Read the root

		e57::E57Root	rootHeader;
		eReader.GetE57Root( rootHeader);		

//Access all the root information like

		char* fileGuid = rootHeader.guid.c_str();
		double fileGPSTime = rootHeader.creationDateTime;
		...

///////////////////////////////////////////////////////////////
// ACCESSING SCAN DATA3D

//Get the number of scan images available

		int data3DCount = eReader.GetData3DCount();

//selecting the first scan

		int scanIndex = 0;

//Read the scan 0 header.

		e57::Data3D		scanHeader;
		eReader.ReadData3D( scanIndex, scanHeader);

//Access all the header information

		_bstr_t bstrName = scanHeader.name.c_str();
		_bstr_t bstrGuid = scanHeader.guid.c_str();
		_bstr_t bstrDesc = scanHeader.description.c_str();

		double startGPSTime = rootHeader.acquisitionStart;
		double endGPSTime = rootHeader.acquisitionEnd;

//Get pose information

		ISI::Point translation;
		translation.x(scanHeader.pose.translation.x);
		translation.y(scanHeader.pose.translation.y);
		translation.z(scanHeader.pose.translation.z);

		ISI::Quat rotation;
		rotation.w(scanHeader.pose.rotation.w);
		rotation.x(scanHeader.pose.rotation.x);
		rotation.y(scanHeader.pose.rotation.y);
		rotation.z(scanHeader.pose.rotation.z);

//Get scanner information

		_bstr_t bstrSerial = scanHeader.sensorSerialNumber.c_str();
		_bstr_t bstrVendor = scanHeader.sensorVendor.c_str();
		_bstr_t bstrModel = scanHeader.sensorModel.c_str();
		_bstr_t bstrSoftware = scanHeader.sensorSoftwareVersion.c_str();
		_bstr_t bstrFirmware = scanHeader.sensorFirmwareVersion.c_str();
		_bstr_t bstrHardware = scanHeader.sensorHardwareVersion.c_str();

//Get environment information

		double temperature = scanHeader.temperature;
		double humidity = scanHeader.relativeHumidity;
		double airPressure = scanHeader.atmosphericPressure;

		...

/////////////////////////////////////////////////////////////////
// ACCESSING SCAN DATA

//Get the Size of the Scan
		int64_t nColumn = 0;	
		int64_t nRow = 0;

		int64_t nPointsSize = 0;	//Number of points

		int64_t nGroupsSize = 0;	//Number of groups
		int64_t nCountSize = 0;		//Number of points per group
		bool	bColumnIndex = false; //indicates that idElementName is "columnIndex"

		eReader.GetData3DSizes( scanIndex, nRow, nColumn, nPointsSize, nGroupsSize, nCountSize, bColumnIndex);

		int64_t nSize = nRow;
		if(nSize == 0) nSize = 1024;	// choose a chunk size

//Setup buffers

		int8_t * isInvalidData = NULL;
		if(scanHeader.pointFields.cartesianInvalidStateField)
			isInvalidData = new int8_t[nSize];

//Setup Points Buffers

		double * xData = NULL;
		if(scanHeader.pointFields.cartesianXField)
			xData = new double[nSize];

		double * yData = NULL;
		if(scanHeader.pointFields.cartesianYField)
			yData = new double[nSize];

		double * zData = NULL;
		if(scanHeader.pointFields.cartesianZField)
			zData = new double[nSize];

//Setup intensity buffers if present

		double *	intData = NULL;
		bool		bIntensity = false;
		double		intRange = 0;
		double		intOffset = 0;

		if(scanHeader.pointFields.intensityField)
		{
			bIntensity = true;
			intData = new double[nSize];
			intRange = scanHeader.intensityLimits.intensityMaximum - scanHeader.intensityLimits.intensityMinimum;
			intOffset = scanHeader.intensityLimits.intensityMinimum;
		}

//Setup color buffers if present

		uint16_t *	redData = NULL;
		uint16_t *	greenData = NULL;
		uint16_t *	blueData = NULL;
		bool		bColor = false;
		int32_t		colorRedRange = 1;
		int32_t		colorRedOffset = 0;
		int32_t		colorGreenRange = 1;
		int32_t		colorGreenOffset = 0;
		int32_t		colorBlueRange = 1;
		int32_t		colorBlueOffset = 0;

		if(header.pointFields.colorRedField)
		{
			bColor = true;
			redData = new uint16_t[nSize];
			greenData = new uint16_t[nSize];
			blueData = new uint16_t[nSize];
			colorRedRange = header.colorLimits.colorRedMaximum - header.colorLimits.colorRedMinimum;
			colorRedOffset = header.colorLimits.colorRedMinimum;
			colorGreenRange = header.colorLimits.colorGreenMaximum - header.colorLimits.colorGreenMinimum;
			colorGreenOffset = header.colorLimits.colorGreenMinimum;
			colorBlueRange = header.colorLimits.colorBlueMaximum - header.colorLimits.colorBlueMinimum;
			colorBlueOffset = header.colorLimits.colorBlueMinimum;
		}

//Setup the GroupByLine buffers information

		int64_t * idElementValue = NULL;
		int64_t * startPointIndex = NULL;
		int64_t * pointCount = NULL;
		if(nGroupsSize > 0)
		{
			idElementValue = new int64_t[nGroupsSize];
			startPointIndex = new int64_t[nGroupsSize];
			pointCount = new int64_t[nGroupsSize];

			if(!eReader.ReadData3DGroupsData(scanIndex, nGroupsSize, idElementValue,
				startPointIndex, pointCount))
				nGroupsSize = 0;
		}

//Setup row/column index information

		int32_t * rowIndex = NULL;
		int32_t * columnIndex = NULL;
		if(scanHeader.pointFields.rowIndexField)
			rowIndex = new int32_t[nSize];
		if(scanHeader.pointFields.columnIndexField)
			columnIndex = new int32_t[nRow];

//Get dataReader object

		e57::CompressedVectorReader dataReader = eReader.SetUpData3DPointsData(
				scanIndex,			//!< data block index given by the NewData3D
				nRow,				//!< size of each of the buffers given
				xData,				//!< pointer to a buffer with the x data
				yData,				//!< pointer to a buffer with the y data
				zData,				//!< pointer to a buffer with the z data
				isInvalidData,		//!< pointer to a buffer with the valid indication
				intData,			//!< pointer to a buffer with the lidar return intesity
				NULL,
				redData,			//!< pointer to a buffer with the color red data
				greenData,			//!< pointer to a buffer with the color green data
				blueData,			//!< pointer to a buffer with the color blue data
				NULL,
				NULL,
				NULL,
				NULL,
				rowIndex,			//!< pointer to a buffer with the rowIndex
				columnIndex			//!< pointer to a buffer with the columnIndex
				);

//Read the point data

		int64_t		count = 0;
		unsigned	size = 0;
		int			col = 0;
		int			row = 0;

		while(size = dataReader.read())
		{

//Use the data

			for(long i = 0; i < size; i++)
			{
				if(columnIndex)
					col = columnIndex[i];
				else
					col = 0;	//point cloud case

				if(rowIndex)
					row = rowIndex[i];
				else
					row = count;	//point cloud case

				if(isInvalidData[i] == 0)
					pScan->SetPoint(row, col, xData[i], yData[i], zData[i]);

				if(bIntensity){		//Normalize intensity to 0 - 1.
					double intensity = (intData[i] - intOffset)/intRange;
					pScan->SetIntensity(row, col, intensity);
				}

				if(bColor){			//Normalize color to 0 - 255
					int red = ((redData[i] - colorRedOffset) * 255)/colorRedRange;
					int green = ((greenData[i] - colorGreenOffset) * 255)/colorBlueRange;
					int blue = ((blueData[i] - colorBlueOffset) * 255)/colorBlueRange;
					pScan->SetColor(row, col, red, green, blue);

				count++;
			}
		}

//Close and clean up
		dataReader.close();

		if(isInvalidData) delete isInvalidData;
		if(xData) delete xData;
		if(yData) delete yData;
		if(zData) delete zData;
		if(intData) delete intData;
		if(redData) delete redData;
		if(greenData) delete greenData;
		if(blueData) delete blueData;

///////////////////////////////////////////////////////////////////////
// ACCESSING PICTURE IMAGE2D

//Get the number of picture images available

		int image2DCount = eReader.GetImage2DCount();

//selecting the first picture image

		int imageIndex = 0;

//Read the picture 0 header.

		e57::Image2D	imageHeader;
		eReader.ReadData3D( imageIndex, imageHeader);

//Access all the header information

		_bstr_t bstrName = imageHeader.name.c_str();
		_bstr_t bstrGuid = imageHeader.guid.c_str();
		_bstr_t bstrDesc = imageHeader.description.c_str();
		double imageGPSTime = rootHeader.acquisitionDateTime;

//Get pose information

		ISI::Point translation;
		translation.x(imageHeader.pose.translation.x);
		translation.y(imageHeader.pose.translation.y);
		translation.z(imageHeader.pose.translation.z);

		ISI::Quat rotation;
		rotation.w(imageHeader.pose.rotation.w);
		rotation.x(imageHeader.pose.rotation.x);
		rotation.y(imageHeader.pose.rotation.y);
		rotation.z(imageHeader.pose.rotation.z);

		//Get camera information

		_bstr_t bstrSerial = imageHeader.sensorSerialNumber.c_str();
		_bstr_t bstrVendor = imageHeader.sensorVendor.c_str();
		_bstr_t bstrModel = imageHeader.sensorModel.c_str();
		...

///////////////////////////////////////////////////////////////////////
// ACCESSING PICTURE IMAGE

//Get the Size of the Picture

		e57::Image2DProjection	imageProjection;	//like E57_SPHERICAL
		e57::Image2DType		imageType;			//like E57_JPEG_IMAGE
		int64_t					nImageWidth = 0;	
		int64_t					nImageHeight = 0;
		int64_t					nImagesSize = 0;	//Number of bytes in the image
		e57::Image2DType		imageMaskType;		//like E57_PNG_IMAGE_MASK if present
		e57::Image2dType		imageVisualType;	//like E57_JPEG_IMAGE if present

		eReader.GetImage2DSizes( imageIndex, imageProjection, imageType,
			nImageWidth, nImageHeight, nImagesSize, imageMaskType, imageVisualType);

//Get pixel information off the sphericalRepresentation if imageProjection == E57_SPHERICAL

		int32_t imageHeight = imageHeader.sphericalRepresentation.imageHeight;
		int32_t imageWidth = imageHeader.sphericalRepresentation.imageWidth;
		double pixelHeight = imageHeader.sphericalRepresentation.pixelHeight;
		double pixelWidth = imageHeader.sphericalRepresentation.pixelWidth;

//Set up buffers

		void* jpegBuffer = new char[nImagesSize];

//Read the picture data

		eReader.ReadImage2DData(imageIndex, imageProjection, imageType, jpegBuffer, 0, nImagesSizw);

// ... access the picture and decode ...

//Close and clean up

		delete jpegBuffer;

		eReaer.Close();	

///////////////////////////////////////////////////////////////////////
// CATCH THE ERRORS

	} catch(E57Exception& ex) {
		ex.report(__FILE__, __LINE__, __FUNCTION__);
	} catch (std::exception& ex) {
		cerr << "Got an std::exception, what=" << ex.what() << endl;
	} catch (...) {
		cerr << "Got an unknown exception" << endl;
	}
</PRE></tt>
@section main_Write_example Writing using the E57 Simple API
An example of a typical use of this interface would be as follows:
<tt><PRE>
	try
	{

// Create a WriterImpl

		_bstr_t bsFile = sFile;			//converts Unicode to UTF-8
		e57::Writer	eWriter( (char*) bsFile);

		if(!eWriter.IsOpen())			//test for being open
			return false;

///////////////////////////////////////////////////////////////
// SETTING UP SCAN DATA3D

		e57::Data3D	scanHeader;

//Setup the Name and Description

		scanHeader.name = (char*) bstrName;
		scanHeader.description = (char*) bstrDesc;

//Setup the GUID

		GUID	guid;					//Window's GUID	
		pScan->GetGuid(&guid);
		OLECHAR wbuffer[64];
		StringFromGUID2(guid,&wbuffer[0],64);
		_bstr_t bstrScanGuid = &wbuffer[0];

		scanHeader.guid = (char*) bstrScanGuid;

		scanHeader.acquisitionStart.SetCurrentGPSTime();	//use real time
		scanHeader.acquisitionEnd.SetCurrentGPSTime();
	
//Setup the scan size

		scanHeader.indexBounds.rowMaximum = nRow - 1;	
		scanHeader.indexBounds.rowMinimum = 0;
		scanHeader.indexBounds.columnMaximum = nColumn - 1;
		scanHeader.indexBounds.columnMinimum = 0;
		scanHeader.indexBounds.returnMaximum = 0; 
		scanHeader.indexBounds.returnMinimum = 0;

//Setup GroupByLine information

		scanHeader.pointGroupingSchemes.groupingByLine.groupsSize = nColumn;
		scanHeader.pointGroupingSchemes.groupingByLine.pointCountSize = nRow;
		scanHeader.pointGroupingSchemes.groupingByLine.idElementName = "columnIndex";

//Set up total number of points

		scanHeader.pointsSize = (nColumn * nRow);	

//Setup bounds

#define DATA_SCALE_FACTOR	(.00001)
		if(exportStatistics)			
		{	//because we are using scaled integers for the data, we must adjust our bounds
			scanHeader.cartesianBounds.xMaximum = floor(pStat->GetMaxX()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;
			scanHeader.cartesianBounds.xMinimum = floor(pStat->GetMinX()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;
			scanHeader.cartesianBounds.yMaximum = floor(pStat->GetMaxY()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;
			scanHeader.cartesianBounds.yMinimum = floor(pStat->GetMinY()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;
			scanHeader.cartesianBounds.zMaximum = floor(pStat->GetMaxZ()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;
			scanHeader.cartesianBounds.zMinimum = floor(pStat->GetMinZ()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;

			scanHeader.sphericalBounds.rangeMaximum = floor(pStat->GetMaxRange()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;
			scanHeader.sphericalBounds.rangeMinimum = floor(pStat->GetMinRange()/DATA_SCALE_FACTOR +0.5) * DATA_SCALE_FACTOR;
			scanHeader.sphericalBounds.azimuthEnd = pStat->GetMaxAzimuth();
			scanHeader.sphericalBounds.azimuthStart = pStat->GetMinAzimuth();
			scanHeader.sphericalBounds.elevationMaximum = pStat->GetMaxPolar();
			scanHeader.sphericalBounds.elevationMinimum = pStat->GetMinPolar();
		}

//Setup pose rotation and transformation

		if(exportMatrix)				
		{
			scanHeader.pose.rotation.w = rotation.w();
			scanHeader.pose.rotation.x = rotation.x();
			scanHeader.pose.rotation.y = rotation.y();
			scanHeader.pose.rotation.z = rotation.z();
			scanHeader.pose.translation.x = translation.x();
			scanHeader.pose.translation.y = translation.y();
			scanHeader.pose.translation.z = translation.z();
		}

//Setup scanner information

		if(exportScanner)
		{
			scanHeader.sensorSerialNumber = (char*) bstrSerial;
			scanHeader.sensorVendor = (char*) bstrVendor;
			scanHeader.sensorModel = (char*) bstrModel;
			scanHeader.sensorSoftwareVersion = (char*) bstrSoftware;
			scanHeader.sensorFirmwareVersion = (char*) bstrFirmware;
			scanHeader.sensorHardwareVersion = (char*) bstrHardware;

			scanHeader.temperature = pStat->GetTemperature();
			scanHeader.relativeHumidity = pStat->GetHumidity();
			scanHeader.atmosphericPressure = pStat->GetAirPressure();
		}
/////////////////////////////////////////////////////////////////
// SETTING UP PointRecord Fields

//Setup Points

		scanHeader.pointFields.cartesianInvalidStateField = true;
		int8_t * isInvalidData = new int8_t[nRow];

		scanHeader.pointFields.cartesianXField = true;
		double * xData = new double[nRow];
		scanHeader.pointFields.cartesianYField = true;
		double * yData = new double[nRow];
		scanHeader.pointFields.cartesianZField = true;
		double * zData = new double[nRow];

		scanHeader.pointFields.pointRangeScaledInteger = DATA_SCALE_FACTOR;
		scanHeader.pointFields.pointRangeMinimum = ((double)-e57::E57_INT32_MAX) / DATA_SCALE_FACTOR;
		scanHeader.pointFields.pointRangeMaximum = ((double) e57::E57_INT32_MAX) / DATA_SCALE_FACTOR;

//Setup Color

		uint16_t * redData = NULL;
		uint16_t * greenData = NULL;
		uint16_t * blueData = NULL;
		if(exportColor)
		{
			scanHeader.pointFields.colorRedField = true;
			redData = new uint16_t[nRow];
			scanHeader.pointFields.colorGreenField = true;
			greenData = new uint16_t[nRow];
			scanHeader.pointFields.colorBlueField = true;
			blueData = new uint16_t[nRow];

			scanheader.colorLimits.colorRedMinimum = e57::E57_UINT8_MIN;
			scanheader.colorLimits.colorRedMaximum = e57::E57_UINT8_MAX;
			scanheader.colorLimits.colorGreenMinimum = e57::E57_UINT8_MIN;
			scanheader.colorLimits.colorGreenMaximum = e57::E57_UINT8_MAX;
			scanheader.colorLimits.colorBlueMinimum = e57::E57_UINT8_MIN;
			scanheader.colorLimits.colorBlueMaximum = e57::E57_UINT8_MAX;
		}

//Setup Intensity

		double * intData = NULL;
		if(exportIntensity)
		{
			scanHeader.pointFields.intensityField = true;
			intData = new double[nRow];

			header.intensityLimits.intensityMaximum = 1.;
			headerintensityLimits.intensityMinimum = 0.;
			header.pointFields.intensityScaledInteger = 0.;		//0. uses FloatNode, -1. uses IntegerNode, >0. uses ScaledIntegerNode
		}

//Write out a new scan header and receive back the index							

		int scanIndex = eWriter.NewData3D(scanHeader);

//Setup the data buffers

		e57::CompressedVectorWriter dataWriter = eWriter.SetUpData3DPointsData(
				scanIndex,			//!< data block index given by the NewData3D
				nRow,				//!< size of each of the buffers given
				xData,				//!< pointer to a buffer with the x data
				yData,				//!< pointer to a buffer with the y data
				zData,				//!< pointer to a buffer with the z data
				isInvalidData,		//!< pointer to a buffer with the valid indication
				intData,			//!< pointer to a buffer with the lidar return intesity
				NULL,
				redData,			//!< pointer to a buffer with the color red data
				greenData,			//!< pointer to a buffer with the color green data
				blueData,			//!< pointer to a buffer with the color blue data
				NULL
				);

/////////////////////////////////////////////////////////////////
// WRITING SCAN DATA

		vector<int64_t>	idElementValue;
		vector<int64_t> startPointIndex;
		vector<int64_t> pointCount;
		int group = 0;
		int startPoint = 0;

//Access the point data

		for(long j = 0; j < nColumn; j++)
		{
			int count = 0;

			for(long i = 0; i < nRow; i++)
			{

//Get the invalid status

				isInvalidData[count] = (pScan->GetStatus(i,j) & INVALID) ? 2 : 0;

//Get the point

				ISI::Point point = pScan->GetPoint(i, j);
				xData[count] = point.x();
				yData[count] = point.y();
				zData[count] = point.z();

//Get the intensity 0. to 1. range

				if(exportIntensity)
					intData[count] = pScan->GetIntensity(i, j);

//Get the color

				if(exportColor)
				{
					uint8_t red = 0;
					uint8_t green = 0;
					uint8_t blue = 0;
					pScan->GetColor(i, j, &red, &green, &blue);
						
					redData[count] = (uint8_t) red;
					greenData[count] = (uint8_t) green;
					blueData[count] = (uint8_t) blue;
				}
				count++;
			}
//Write out the buffers

			dataWriter.write(count);

//Collect the group information

			idElementValue.push_back((int64_t) j);
			startPointIndex.push_back((int64_t) startPoint);
			pointCount.push_back((int64_t) count);
			group++;

			startPoint += count;
		}

//Finish the scan data write

		dataWriter.close();

//Write out the group information

		eWriter.WriteData3DGroupsData(scanIndex, group,
			(int64_t*) &idElementValue[0],
			(int64_t*) &startPointIndex[0],
			(int64_t*) &pointCount[0]);

//Clean up

		if(isInvalidData) delete isInvalidData;
		if(xData) delete xData;
		if(yData) delete yData;
		if(zData) delete zData;
		if(intData) delete intData;
		if(redData) delete redData;
		if(greenData) delete greenData;
		if(blueData) delete blueData;

///////////////////////////////////////////////////////////////
// SETTING UP SCAN IMAGE2D

		e57::Image2D	imageHeader;

//Setup the Name and Description											

		imageHeader.name = (char*) bstrName;
		imageHeader.description = (char*) bstrDesc;

//Setup the GUID

		imageHeader.guid = (char*) bstrImageGuid;
		imageHeader.associatedData3DGuid = (char*) bstrScanGuid;

//Setup the DateTime

		imageHeader.acquisitionDateTime.SetCurrentGPSTime() //set current time.

//Setup camera information

		imageHeader.sensorSerialNumber = (char*) bstrSerial;
		imageHeader.sensorVendor = (char*) bstrVendor;
		imageHeader.sensorModel = (char*) bstrModel;

//Setup image orientation

		imageHeader.pose.rotation.w = rotation.w();
		imageHeader.pose.rotation.x = rotation.x();
		imageHeader.pose.rotation.y = rotation.y();
		imageHeader.pose.rotation.z = rotation.z();
		imageHeader.pose.translation.x = translation.x();
		imageHeader.pose.translation.y = translation.y();
		imageHeader.pose.translation.z = translation.z();

//Setup image size

		imageHeader.sphericalRepresentation.imageHeight = imageHeight;
		imageHeader.sphericalRepresentation.imageWidth = imageWidth;
		imageHeader.sphericalRepresentation.pixelHeight = pixelHeight;
		imageHeader.sphericalRepresentation.pixelWidth = pixelWidth;

//Open jpeg file

		CFile jpegFile;
		CFileException fileError;
		jpegFile.Open(imagePath, CFile::modeRead | CFile::typeBinary, &fileError);
		ULONGLONG dwLength = jpegFile.GetLength();

		imageHeader.sphericalRepresentation.jpegImageSize = dwLength; //Real Size before newing image header

//Write the image header

		int imageIndex = eWriter.NewImage2D(header);

//Access the jpeg image data

		void * pBuffer = new char[dwLength];
		jpegFile.SeekToBegin();
		jpegFile.Read(pBuffer,dwLength);

//Write the jpeg data 

		eWriter.WriteImage2DData(imageIndex, e57::E57_JPEG_IMAGE, e57::E57_SPHERICAL,
					pBuffer,0,dwLength);

//Finish the E57 file

		eWriter.Close();	

///////////////////////////////////////////////////////////////////////
// CATCH THE ERRORS

	} catch(E57Exception& ex) {
		ex.report(__FILE__, __LINE__, __FUNCTION__);
	} catch (std::exception& ex) {
		cerr << "Got an std::exception, what=" << ex.what() << endl;
	} catch (...) {
		cerr << "Got an unknown exception" << endl;
	}
</PRE></tt>

@page CopyRightPage Copyright

Copyright 2010 ASTM E57.04 3D Imaging System File Format Committee
 
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

//! @file E57Simple.cpp

#if defined(WIN32)
#  if defined(_MSC_VER)
#    include <io.h>
#    include <fcntl.h>
#    include <sys\stat.h>
#    include <windows.h>
#  elif defined(__GNUC__)
#  define _LARGEFILE64_SOURCE
#  define __LARGE64_FILES
#  include <sys/types.h>
#  include <unistd.h>

#    include <fcntl.h>
#    include <sys\stat.h>
#  else
#    error "no supported compiler defined"
#  endif
#elif defined(LINUX)
#  define _LARGEFILE64_SOURCE
#  define __LARGE64_FILES
#  include <sys/types.h>
#  include <unistd.h>
#else
#  error "no supported OS platform defined"
#endif

#include "E57Simple.h"
#include "E57SimpleImpl.h"

using namespace e57;
using namespace std;
using namespace boost;

////////////////////////////////////////////////////////////////////
//
//	e57::DateTime
//
	DateTime::DateTime(void)
{
	dateTimeValue = 0.;
	isAtomicClockReferenced = 0;
};

	DateTime::~DateTime(void)
{
};

void	DateTime::SetCurrentGPSTime(void)
{
	dateTimeValue = e57::GetGPSTime();
	isAtomicClockReferenced = 0;
};

void	DateTime::SetUTCDateTime(
	int utc_year,		//!< The year 1900-9999
	int utc_month,		//!< The month 0-11
	int utc_day,		//!< The day 1-31
	int utc_hour,		//!< The hour 0-23
	int utc_minute,		//!< The minute 0-59
	float utc_seconds	//!< The seconds 0.0 - 59.999
	)
{
	dateTimeValue = e57::GetGPSDateTimeFromUTC(utc_year, utc_month,
		utc_day, utc_hour, utc_minute, utc_seconds);

	isAtomicClockReferenced = 0;
};

void	DateTime::GetUTCDateTime(
	int &utc_year,		//!< The year 1900-9999
	int &utc_month,		//!< The month 0-11
	int &utc_day,		//!< The day 1-31
	int &utc_hour,		//!< The hour 0-23
	int &utc_minute,	//!< The minute 0-59
	float &utc_seconds	//!< The seconds 0.0 - 59.999
	) const
{
	e57::GetUTCFromGPSDateTime(dateTimeValue, utc_year, utc_month,
		utc_day, utc_hour, utc_minute, utc_seconds);
};
#if defined(WIN32)

void	DateTime::SetSystemTime(
	SYSTEMTIME	sysTim		//!< Windows System Time
	)
{
	dateTimeValue = e57::GetGPSDateTimeFromSystemTime(sysTim);
	isAtomicClockReferenced = 0;
};

void	DateTime::GetSystemTime(
	SYSTEMTIME	&sysTim		//!< Windows System Time
	)
{
	e57::GetSystemTimeFromGPSDateTime(dateTimeValue,sysTim);
};
#endif
////////////////////////////////////////////////////////////////////
//
//	e57::E57Root
//
	E57Root::E57Root(void)
{
	Reset();
};

	E57Root::~E57Root(void)
{
};

void E57Root::Reset(void)
{
	versionMajor = 1;
	versionMinor = 0;
	creationDateTime.dateTimeValue = 0.;
	creationDateTime.isAtomicClockReferenced = 0;
	data3DSize = 0;
	images2DSize = 0;
};
////////////////////////////////////////////////////////////////////
//
//	e57::Data3D
//
	Data3D::Data3D(void)
{
	Reset();
};

	Data3D::~Data3D(void)
{
};
void Data3D::Reset(void)
{
	originalGuids.clear();
	temperature = E57_FLOAT_MAX;
	relativeHumidity = E57_FLOAT_MAX;
	atmosphericPressure = E57_FLOAT_MAX;

	acquisitionStart.dateTimeValue = 0.;
	acquisitionStart.isAtomicClockReferenced = 0;
	acquisitionEnd.dateTimeValue = 0.;
	acquisitionEnd.isAtomicClockReferenced = 0;

	pose.rotation.w = 1.;
	pose.rotation.x = 0.;
	pose.rotation.y = 0.;
	pose.rotation.z = 0.;
	pose.translation.x = 0.;
	pose.translation.y = 0.;
	pose.translation.z = 0.;

	cartesianBounds.xMaximum = E57_DOUBLE_MAX;
	cartesianBounds.xMinimum = -E57_DOUBLE_MAX;
	cartesianBounds.yMaximum = E57_DOUBLE_MAX;
	cartesianBounds.yMinimum = -E57_DOUBLE_MAX;
	cartesianBounds.zMaximum = E57_DOUBLE_MAX;
	cartesianBounds.zMinimum = -E57_DOUBLE_MAX;

	sphericalBounds.rangeMinimum = 0.;
	sphericalBounds.rangeMaximum = E57_DOUBLE_MAX;
	sphericalBounds.azimuthStart = -PI;
	sphericalBounds.azimuthEnd = PI;
	sphericalBounds.elevationMinimum = -PI/2.;
	sphericalBounds.elevationMaximum = PI/2.;

	indexBounds.rowMinimum = 0;
	indexBounds.rowMaximum = 0;
	indexBounds.columnMinimum = 0;
	indexBounds.columnMaximum = 0;
	indexBounds.returnMinimum = 0;
	indexBounds.returnMaximum = 0;

	intensityLimits.intensityMinimum = 0.;
	intensityLimits.intensityMaximum = 0.;

	colorLimits.colorRedMinimum = 0.;
	colorLimits.colorRedMaximum = 0.;
	colorLimits.colorGreenMinimum = 0.;
	colorLimits.colorGreenMaximum = 0.;
	colorLimits.colorBlueMinimum = 0.;
	colorLimits.colorBlueMaximum = 0.;

	pointGroupingSchemes.groupingByLine.groupsSize = 0;
	pointGroupingSchemes.groupingByLine.pointCountSize = 0;
	pointGroupingSchemes.groupingByLine.idElementName = "";

	pointFields.cartesianXField = false;
	pointFields.cartesianYField = false;
	pointFields.cartesianZField = false;
	pointFields.cartesianInvalidStateField = false;

	pointFields.pointRangeScaledInteger = 0.;
	pointFields.pointRangeMinimum = E57_FLOAT_MIN;
	pointFields.pointRangeMaximum = E57_FLOAT_MAX;

	pointFields.sphericalRangeField = false;
	pointFields.sphericalAzimuthField = false;
	pointFields.sphericalElevationField = false;
	pointFields.sphericalInvalidStateField = false;

	pointFields.angleScaledInteger = 0.;
	pointFields.angleMinimum = E57_FLOAT_MIN;
	pointFields.angleMaximum = E57_FLOAT_MAX;

	pointFields.colorBlueField = false;
	pointFields.colorGreenField = false;
	pointFields.colorRedField = false;
	pointFields.isColorInvalidField = false;

	pointFields.columnIndexField = false;
	pointFields.columnIndexMaximum = E57_UINT32_MAX;
	pointFields.rowIndexField = false;
	pointFields.rowIndexMaximum = E57_UINT32_MAX;

	pointFields.returnCountField = false;
	pointFields.returnIndexField = false;
	pointFields.returnMaximum = E57_UINT8_MAX;

	pointFields.intensityField = false;
	pointFields.isIntensityInvalidField = false;
	pointFields.intensityScaledInteger = -1.;	//Default to -1 means IntegerNode, 0. means FloatNode, and >0. means ScaleIntegerNode

	pointFields.timeStampField = false;
	pointFields.isTimeStampInvalidField = false;
	pointFields.timeMaximum = E57_DOUBLE_MAX;
	pointsSize = 0;
};
////////////////////////////////////////////////////////////////////
//
//	e57::Image2D
//
	Image2D::Image2D(void)
{
	Reset();
};

	Image2D::~Image2D(void)
{
};

void Image2D::Reset(void)
{
	acquisitionDateTime.dateTimeValue = 0.;
	acquisitionDateTime.isAtomicClockReferenced = 0;

	pose.rotation.w = 1.;
	pose.rotation.x = 0.;
	pose.rotation.y = 0.;
	pose.rotation.z = 0.;
	pose.translation.x = 0.;
	pose.translation.y = 0.;
	pose.translation.z = 0.;

	visualReferenceRepresentation.imageMaskSize = 0;
	visualReferenceRepresentation.pngImageSize = 0;
	visualReferenceRepresentation.jpegImageSize = 0;
	visualReferenceRepresentation.imageHeight = 0;
	visualReferenceRepresentation.imageWidth = 0;

	pinholeRepresentation.jpegImageSize = 0;
	pinholeRepresentation.pngImageSize = 0;
	pinholeRepresentation.imageMaskSize = 0;
	pinholeRepresentation.focalLength = 0.;
	pinholeRepresentation.imageHeight = 0;
	pinholeRepresentation.imageWidth = 0;
	pinholeRepresentation.pixelHeight = 0;
	pinholeRepresentation.pixelWidth = 0;
	pinholeRepresentation.principalPointX = 0;
	pinholeRepresentation.principalPointY = 0;

	sphericalRepresentation.jpegImageSize = 0;
	sphericalRepresentation.pngImageSize = 0;
	sphericalRepresentation.imageMaskSize = 0;
	sphericalRepresentation.imageHeight = 0;
	sphericalRepresentation.imageWidth = 0;
	sphericalRepresentation.pixelHeight = 0;
	sphericalRepresentation.pixelWidth = 0;

	cylindricalRepresentation.jpegImageSize = 0;
	cylindricalRepresentation.pngImageSize = 0;
	cylindricalRepresentation.imageMaskSize = 0;
	cylindricalRepresentation.imageHeight = 0;
	cylindricalRepresentation.imageWidth = 0;
	cylindricalRepresentation.pixelHeight = 0;
	cylindricalRepresentation.pixelWidth = 0;
	cylindricalRepresentation.principalPointY = 0;
	cylindricalRepresentation.radius = 0;
};
////////////////////////////////////////////////////////////////////
//
//	e57::Reader
//
			Reader :: Reader(const ustring & filePath)
: impl_(new ReaderImpl(filePath))
{
}

bool		Reader :: IsOpen(void) const
{
	return impl_->IsOpen();
};

bool		Reader :: Close(void) const
{
	return impl_->Close();
};

bool		Reader :: GetE57Root(
	E57Root & fileHeader) const
{
	return impl_->GetE57Root(fileHeader);
};

int32_t		Reader :: GetImage2DCount( void) const
{
	return impl_->GetImage2DCount();
};

bool		Reader :: ReadImage2D( 
	int32_t			imageIndex,
	Image2D &	image2DHeader) const
{
	return impl_->ReadImage2D(imageIndex,image2DHeader);
};

bool		Reader :: GetImage2DSizes(
	int32_t					imageIndex,		// This in the index into the image2D vector
	e57::Image2DProjection &imageProjection,// identifies the projection desired.
	e57::Image2DType &		imageType,		// identifies the image format desired.
	int64_t &				imageWidth,		// The image width (in pixels).
	int64_t &				imageHeight,	// The image height (in pixels).
	int64_t &				imageSize,		// This is the total number of bytes for the image blob.
	e57::Image2DType &		imageMaskType,	// This is E57_PNG_IMAGE_MASK if "imageMask" is defined in the projection
	e57::Image2DType &		imageVisualType	// This is image type of the VisualReferenceRepresentation if given.
	) const
{
	return impl_->GetImage2DSizes(imageIndex, imageProjection, imageType,
		imageWidth, imageHeight, imageSize, imageMaskType, imageVisualType);
};

int64_t		Reader :: ReadImage2DData(
	int32_t					imageIndex,		// picture block index
	e57::Image2DProjection	imageProjection,// identifies the projection desired.
	e57::Image2DType		imageType,		// identifies the image format desired.
	void *					pBuffer,	// pointer the buffer
	int64_t					start,		// position in the block to start reading
	int64_t					count		// size of desired chuck or buffer size
	) const
{
	return impl_->ReadImage2DData(imageIndex, imageProjection, imageType, pBuffer, start, count);
};

int32_t		Reader :: GetData3DCount( void) const
{
	return impl_->GetData3DCount();
};

// This function returns the file raw E57Root Structure Node
StructureNode	Reader :: GetRawE57Root(void)
{
	return impl_->GetRawE57Root();
};	// /return Returns the E57Root StructureNode

// This function returns the raw Data3D Vector Node
VectorNode		Reader :: GetRawData3D(void)
{
	return impl_->GetRawData3D();
};// /return Returns the raw Data3D VectorNode

// This function returns the raw Images2D Vector Node
VectorNode		Reader :: GetRawImages2D(void)
{
	return impl_->GetRawImages2D();
};	// /return Returns the raw Image2D VectorNode

bool		Reader :: ReadData3D( 
	int32_t		dataIndex,	// This in the index into the images3D vector
	Data3D &	data3DHeader // pointer to the Data3D structure to receive the image information
	) const	// /return Returns true if sucessful
{
	return impl_->ReadData3D(dataIndex, data3DHeader);
}

bool		Reader :: GetData3DSizes(
	int32_t		dataIndex,	// This in the index into the images3D vector
	int64_t &	rowMax,		// This is the maximum row size
	int64_t &	columnMax,	// This is the maximum column size
	int64_t &	pointsSize,	// This is the total number of point records
	int64_t &	groupsSize,	// This is the total number of group reocrds
	int64_t &	countSize,	//!< This is the maximum point count per group
	bool &		bColumnIndex	//!< This indicates that the idElementName is "columnIndex"
	) const
{
	return impl_->GetData3DSizes( dataIndex, rowMax, columnMax, pointsSize,
		groupsSize, countSize, bColumnIndex);
}

bool		Reader :: ReadData3DGroupsData(
	int32_t		dataIndex,			// data block index given by the NewData3D
	int32_t		groupCount,			// size of each of the buffers given
	int64_t*	idElementValue,		// index for this group
	int64_t*	startPointIndex,	// Starting index in to the "points" data vector for the groups
	int64_t*	pointCount			// size of the groups given
	) const							// \return Return true if sucessful, false otherwise
{
	return impl_->ReadData3DGroupsData(dataIndex, groupCount, idElementValue, startPointIndex, pointCount);
}

CompressedVectorReader	Reader :: SetUpData3DPointsData(
	int32_t		dataIndex,			// data block index given by the NewData3D
	int64_t		pointCount,			// size of each element buffer.
	double*		cartesianX,			//!< pointer to a buffer with the X coordinate (in meters) of the point in Cartesian coordinates
	double*		cartesianY,			//!< pointer to a buffer with the Y coordinate (in meters) of the point in Cartesian coordinates
	double*		cartesianZ,			//!< pointer to a buffer with the Z coordinate (in meters) of the point in Cartesian coordinates
	int8_t*		cartesianInvalidState,	//!< Value = 0 if the point is considered valid, 1 otherwise

	double*		intensity,			//!< pointer to a buffer with the Point response intensity. Unit is unspecified
	int8_t*		isIntensityInvalid,	//!< Value = 0 if the intensity is considered valid, 1 otherwise

	uint16_t*	colorRed,			//!< pointer to a buffer with the Red color coefficient. Unit is unspecified
	uint16_t*	colorGreen,			//!< pointer to a buffer with the Green color coefficient. Unit is unspecified
	uint16_t*	colorBlue,			//!< pointer to a buffer with the Blue color coefficient. Unit is unspecified
	int8_t*		isColorInvalid,		//!< Value = 0 if the color is considered valid, 1 otherwise

	double*		sphericalRange,		//!< pointer to a buffer with the range (in meters) of points in spherical coordinates. Shall be non-negative
	double*		sphericalAzimuth,	//!< pointer to a buffer with the Azimuth angle (in radians) of point in spherical coordinates
	double*		sphericalElevation,	//!< pointer to a buffer with the Elevation angle (in radians) of point in spherical coordinates
	int8_t*		sphericalInvalidState, //!< Value = 0 if the range is considered valid, 1 otherwise

	int32_t*	rowIndex,			//!< pointer to a buffer with the row number of point (zero based). This is useful for data that is stored in a regular grid.Shall be in the interval (0, 2^63).
	int32_t*	columnIndex,		//!< pointer to a buffer with the column number of point (zero based). This is useful for data that is stored in a regular grid. Shall be in the interval (0, 2^63).
	int8_t*		returnIndex,		//!< pointer to a buffer with the number of this return (zero based). That is, 0 is the first return, 1 is the second, and so on. Shall be in the interval (0, returnCount). Only for multi-return sensors. 
	int8_t*		returnCount,		//!< pointer to a buffer with the total number of returns for the pulse that this corresponds to. Shall be in the interval (0, 2^63). Only for multi-return sensors. 

	double*		timeStamp,			//!< pointer to a buffer with the time (in seconds) since the start time for the data, which is given by acquisitionStart in the parent Data3D Structure. Shall be non-negative
	int8_t*		isTimeStampInvalid	//!< Value = 0 if the timeStamp is considered valid, 1 otherwise
	) const
{
	return impl_->SetUpData3DPointsData( dataIndex, pointCount,
		cartesianX, cartesianY, cartesianZ, cartesianInvalidState,
		intensity, isIntensityInvalid,
		colorRed, colorGreen, colorBlue, isColorInvalid,
		sphericalRange, sphericalAzimuth, sphericalElevation, sphericalInvalidState,
		rowIndex, columnIndex, returnIndex, returnCount,
		timeStamp, isTimeStampInvalid);
}

////////////////////////////////////////////////////////////////////
//
//	e57::Writer
//
			Writer :: Writer(
	const ustring & filePath,		// file path string
	const ustring & coordinateMetaData	// Information describing the Coordinate Reference System to be used for the file
): impl_(new WriterImpl(filePath, coordinateMetaData))
{
}

bool		Writer :: IsOpen(void) const
{
	return impl_->IsOpen();
};

bool		Writer :: Close(void) const
{
	return impl_->Close();
};
// This function returns the file raw E57Root Structure Node
StructureNode	Writer :: GetRawE57Root(void)
{
	return impl_->GetRawE57Root();
};	// /return Returns the E57Root StructureNode

// This function returns the raw Data3D Vector Node
VectorNode		Writer :: GetRawData3D(void)
{
	return impl_->GetRawData3D();
};// /return Returns the raw Data3D VectorNode

// This function returns the raw Images2D Vector Node
VectorNode		Writer :: GetRawImages2D(void)
{
	return impl_->GetRawImages2D();
};	// /return Returns the raw Image2D VectorNode

int32_t		Writer :: NewImage2D( 
	Image2D &	image2DHeader	// pointer to the Image2D structure to receive the picture information
	) const						// /return Returns the image2D index
{
	return impl_->NewImage2D( image2DHeader);
};


int64_t		Writer :: WriteImage2DData(
	int32_t					imageIndex,		// picture block index given by the NewImage2D
	e57::Image2DType		imageType,		// identifies the image format desired.
	e57::Image2DProjection	imageProjection,// identifies the projection desired.
	void *					pBuffer,		// pointer the buffer
	int64_t					start,			// position in the block to start writing
	int64_t					count			// size of desired chuck or buffer size
	) const									// /return Returns the number of bytes written
{
	return impl_->WriteImage2DData( imageIndex, imageType, imageProjection, pBuffer, start, count);
};

int32_t		Writer :: NewData3D( 
	Data3D &	data3DHeader	// pointer to the Data3D structure to receive the image information
	) const							// /return Returns the index of the new scan's data3D block.
{
	return impl_->NewData3D( data3DHeader);
};

// This function writes out blocks of point data
CompressedVectorWriter	Writer :: SetUpData3DPointsData(
	int32_t		dataIndex,			// data block index given by the NewData3D
	int64_t		pointCount,			// size of each of the buffers given
	double*		cartesianX,			//!< pointer to a buffer with the X coordinate (in meters) of the point in Cartesian coordinates
	double*		cartesianY,			//!< pointer to a buffer with the Y coordinate (in meters) of the point in Cartesian coordinates
	double*		cartesianZ,			//!< pointer to a buffer with the Z coordinate (in meters) of the point in Cartesian coordinates
	int8_t*		cartesianInvalidState,	//!< Value = 0 if the point is considered valid, 1 otherwise

	double*		intensity,			//!< pointer to a buffer with the Point response intensity. Unit is unspecified
	int8_t*		isIntensityInvalid,	//!< Value = 0 if the intensity is considered valid, 1 otherwise

	uint16_t*	colorRed,			//!< pointer to a buffer with the Red color coefficient. Unit is unspecified
	uint16_t*	colorGreen,			//!< pointer to a buffer with the Green color coefficient. Unit is unspecified
	uint16_t*	colorBlue,			//!< pointer to a buffer with the Blue color coefficient. Unit is unspecified
	int8_t*		isColorInvalid,		//!< Value = 0 if the color is considered valid, 1 otherwise

	double*		sphericalRange,		//!< pointer to a buffer with the range (in meters) of points in spherical coordinates. Shall be non-negative
	double*		sphericalAzimuth,	//!< pointer to a buffer with the Azimuth angle (in radians) of point in spherical coordinates
	double*		sphericalElevation,	//!< pointer to a buffer with the Elevation angle (in radians) of point in spherical coordinates
	int8_t*		sphericalInvalidState, //!< Value = 0 if the range is considered valid, 1 otherwise

	int32_t*	rowIndex,			//!< pointer to a buffer with the row number of point (zero based). This is useful for data that is stored in a regular grid.Shall be in the interval (0, 2^63).
	int32_t*	columnIndex,		//!< pointer to a buffer with the column number of point (zero based). This is useful for data that is stored in a regular grid. Shall be in the interval (0, 2^63).
	int8_t*		returnIndex,		//!< pointer to a buffer with the number of this return (zero based). That is, 0 is the first return, 1 is the second, and so on. Shall be in the interval (0, returnCount). Only for multi-return sensors. 
	int8_t*		returnCount,		//!< pointer to a buffer with the total number of returns for the pulse that this corresponds to. Shall be in the interval (0, 2^63). Only for multi-return sensors. 

	double*		timeStamp,			//!< pointer to a buffer with the time (in seconds) since the start time for the data, which is given by acquisitionStart in the parent Data3D Structure. Shall be non-negative
	int8_t*		isTimeStampInvalid	//!< Value = 0 if the timeStamp is considered valid, 1 otherwise
	) const
{
		return impl_->SetUpData3DPointsData( dataIndex, pointCount,
			cartesianX, cartesianY, cartesianZ, cartesianInvalidState,
			intensity, isIntensityInvalid,
			colorRed, colorGreen, colorBlue, isColorInvalid,
			sphericalRange, sphericalAzimuth, sphericalElevation, sphericalInvalidState,
			rowIndex, columnIndex, returnIndex, returnCount,
			timeStamp, isTimeStampInvalid);
}

bool		Writer :: WriteData3DGroupsData(
	int32_t		dataIndex,			// data block index given by the NewData3D
	int32_t		groupCount,			//!< size of each of the buffers given
	int64_t*	idElementValue,		// index for this group
	int64_t*	startPointIndex,	// Starting index in to the "points" data vector for the groups
	int64_t*	pointCount			// size of the groups given
	) const								// \return Return true if sucessful, false otherwise
{
	return impl_->WriteData3DGroupsData(
		dataIndex, groupCount, idElementValue, startPointIndex, pointCount);
}

